﻿using Caliburn.Micro;

namespace SS.WPF.Framework.Localization
{
    using System;
    using System.Globalization;
    using System.Windows;
    using System.Windows.Data;
    using System.Windows.Markup;

    /// <summary>
    /// The Translate Markup extension returns a binding to a TranslationData
    /// that provides a translated resource of the specified key
    /// Refer the link as below for more detail abount Custom MarkupExtension
    /// http://www.codeproject.com/Articles/140618/WPF-Tutorial-TypeConverter-Markup-Extension
    /// </summary>
    public class TranslateExtension : MarkupExtension
    {
        #region Properties

        private string _key;
        [ConstructorArgument("key")]
        public string Key
        {
            get { return _key; }
            set { _key = value; }
        }

        private Binding _binding;
        public Binding Binding
        {
            get
            {
                return this._binding;
            }
            set
            {
                this._binding = value;
            }
        }

        public IValueConverter Converter { get; set; }

        #endregion

        #region Construction

        public TranslateExtension()
        {
        }

        /// <summary>
        /// Initializes a new instance of the <see cref="TranslateExtension"/> class.
        /// </summary>
        /// <param name="key">The key.</param>
        public TranslateExtension(string key)
        {
            _key = key;
        }      

        #endregion

        /// <summary>
        /// Returns an object that is set as the
        //  value of the target property for this markup extension.
        /// See <see cref="MarkupExtension.ProvideValue" />
        /// </summary>
        public override object ProvideValue(IServiceProvider serviceProvider)
        {
            object result;

            if (this._binding != null)
            {
                // Using MultiBinding when the key is not assigned value at compile time, 
                // It will be set value at runtime
                // The Converter is used to compile values from multi Binding to unique value
                var multiBinding = new MultiBinding
                                       {
                                           Converter = TranslationMultiValueConverter,
                                       };

                multiBinding.Bindings.Add(this._binding);
                multiBinding.Bindings.Add(new Binding("Value")
                                              {
                                                  Source = new TranslationData(_key),
                                              });

                result = multiBinding.ProvideValue(serviceProvider);
            }
            else
            {
                var binding = new Binding("Value")
                                  {
                                      Source = new TranslationData(_key)
                                  };
                result = binding.ProvideValue(serviceProvider);
            }

            return result;
        }

        private static readonly TranslationMultiValueConverter TranslationMultiValueConverter =
            new TranslationMultiValueConverter();
    }

    class TranslationMultiValueConverter : IMultiValueConverter
    {
        #region Implementation of IMultiValueConverter

        /// <summary>
        /// Converts source values to a value for the binding target. The data binding engine calls this method when it propagates the values from source bindings to the binding target.
        /// </summary>
        /// <returns>
        /// A converted value.If the method returns null, the valid null value is used.A return value of <see cref="T:System.Windows.DependencyProperty"/>.<see cref="F:System.Windows.DependencyProperty.UnsetValue"/> indicates that the converter did not produce a value, and that the binding will use the <see cref="P:System.Windows.Data.BindingBase.FallbackValue"/> if it is available, or else will use the default value.A return value of <see cref="T:System.Windows.Data.Binding"/>.<see cref="F:System.Windows.Data.Binding.DoNothing"/> indicates that the binding does not transfer the value or use the <see cref="P:System.Windows.Data.BindingBase.FallbackValue"/> or the default value.
        /// </returns>
        /// <param name="values">The array of values that the source bindings in the <see cref="T:System.Windows.Data.MultiBinding"/> produces. The value <see cref="F:System.Windows.DependencyProperty.UnsetValue"/> indicates that the source binding has no value to provide for conversion.</param><param name="targetType">The type of the binding target property.</param><param name="parameter">The converter parameter to use.</param><param name="culture">The culture to use in the converter.</param>
        public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture)
        {
            var key = values[0] as string;
            return IoC.Get<TranslationManager>().Translate(key);
        }

        /// <summary>
        /// Converts a binding target value to the source binding values.
        /// </summary>
        /// <returns>
        /// An array of values that have been converted from the target value back to the source values.
        /// </returns>
        /// <param name="value">The value that the binding target produces.</param><param name="targetTypes">The array of types to convert to. The array length indicates the number and types of values that are suggested for the method to return.</param><param name="parameter">The converter parameter to use.</param><param name="culture">The culture to use in the converter.</param>
        public object[] ConvertBack(object value, Type[] targetTypes, object parameter, CultureInfo culture)
        {
            throw new NotImplementedException();
        }

        #endregion
    }

    class BindingEvaluator : FrameworkElement
    {
        public object Value
        {
            get
            {
                Value = DependencyProperty.UnsetValue;
                SetBinding(BindingEvaluator.ValueProperty, _binding);
                return (object)GetValue(ValueProperty);
            }
            private set { SetValue(ValueProperty, value); }
        }

        static readonly DependencyProperty ValueProperty =
            DependencyProperty.Register("Value", typeof(object), typeof(BindingEvaluator), null);

        readonly Binding _binding;

        public BindingEvaluator(Binding binding)
        {
            _binding = binding;
        }
    }
}
